Направо към съдържанието

Преработка на код

от Уикипедия, свободната енциклопедия

Преработка на код или рефакторинг (на английски: refactoring) е процес на изменение на съществуващата вътрешна структура на кода, без да се променя неговият режим на работа.[1] Прави се с цел подобряване на някои нефункционални елементи на софтуера. Предимствата включват подобрена четливост на кода, намаляване на сложността при експлоатацията на изходния програмен код, по-изразителна вътрешна структура и подобряване на възможността за разширяване.

Непрекъснатото подобряване в проектирането на кода се прави с цел да се постигне по-лесна работа с него. Това е в рязък контраст с това, което се случва обикновено: малко рефакторинг и по-голямо внимание спомагат за целесъобразното добавяне на нови функции. Ако се извършва рефакторинг непрекъснато, по-лесно е поддържането и разширяването на съществуващия код.

Joshua Kerievsky, Refactoring to Patterns[2]

Обикновено рефакторингът се извършва чрез прилагане на серия от стандартизирани основни микро-рефакторинги, всеки от които е обикновено малка промяна в изходния програмен код, което или запазва поведението на софтуера, или най-малко не променя неговото съответствие с функционалните му изисквания. Много среди за разработка на софтуери поддържат функционалност, позволяваща автоматичното извършване на тези основни преработки на кода.

Рефакторингът има за цел да направи програмен код, който е лесен за разбиране. Отличава се от оптимизацията на производителността. Също като нея не променя поведението на програмата, но ускоряват нейния ход. Оптимизацията затруднява разбирането на кода, което е противоположно на рефакторинга.

Причини за използването на рефакторинга

[редактиране | редактиране на кода]

Рефакторингът е нужно да се използва постоянно при разработването на кода. Основните стимули за използването му са:

  1. необходимостта да се добави нова функция, която не е указана в приетия първоначално код
  2. необходимостта да се оправи грешка, която в началото не се вижда
  3. преодоляване на трудности при разработката, които са обусловени от сложни логически програми.

Признаци за „лош“ код

[редактиране | редактиране на кода]

Рефакторингът в много случаи е въз основа на интуицията, обоснована от опита. Има се предвид някои видими проблеми в кода (на английски: code smells):

  • дублиране на кода (на английски: duplicate code)
  • дълъг метод
  • по-висок клас
  • дълъг списък от параметри
  • „завистливи“ функции – това е метод, който обикновено се обръща към данни от друг обект
  • излишни временни променливи
  • класове данни
  • негрупирани данни

Чрез рефакторинга може да се открият слабости в дизайна, които може да забавят развитието на кода или повишават риска от грешки в бъдеще.[3] Например, методът на ръка (ръчен метод) може да бъде много дълъг или може да е близо до дублиране с друг близък метод. Веднъж разпознати тези проблеми може да бъдат разрешени от рефакторинга на изходния програмен код или да бъдат преобразувани в нова форма, която води до същия резултат както и преди, но без да има вече заложени скрити слабости. За по-дълга рутинна проверка могат да бъдат подбрани една или повече по-малки подпрограми; при повторна проверка те могат да бъдат заменени или премахнати с една обща функция. Неизпълнението на рефакторинг може да доведе до натрупването на технически дълг (на английски: technical debt).

Има две основни ползи от извършването на рефакторинг:

  1. Поддръжка. По-лесно е да се коригират грешки в изходния програмен код, който е лесен за разчитане и намеренията на създателят му са лесни за разбиране.[4] Това може да бъде постигнато чрез намаляване на големи монолитни практики в набор от отделни, ясно-наименувани и кратки методи, както и чрез преместване на даден метод в по-подходящ клас или чрез отстраняване на подвеждащи коментари.
  2. Разтегливост. По-лесно е да се разширят възможностите в наличния код, ако се използват разпознаваеми структурни модели (на английски: design patterns), което осигурява известна гъвкавост, която преди не е съществувала.[2]
  3. Преди прилагане на рефакторинг към коя и да е част на кода трябва да има обоснован, убедителен набор от автоматични блок тестове(на английски: unit testing). Тестовете се използват, за да се докаже, че поведението на кода е коректно преди използването на рефакторинг. Ако случайно се окаже, че теста е неуспешен, най-добре е първо да се коригира теста, тъй като в противен случай е трудно да се направи разграничение между грешките в резултат на рефакторинга и вече съществуващите такива. След като се направи рефакторинга, тестовете се провеждат отново, за да се провери дали рефакторинга не е „счупил“ теста. Разбира се, тестовете никога не могат да докажат, че няма грешки, но важното е, че този процес може да бъде икономически по-ефективен от гледна точка на разходите: единични тестове могат да уловят достатъчно грешки и да направят рефакторинга достатъчно безопасен.

След това в процес е един повтарящ се цикъл за извършване на малки трансформации, промени. Тестването на тези трансформации е с цел да се осигури коректност. Ако в някакъв момент теста се провали, последната малка промяна се отменя и се реализира по различен начин. Чрез много малки стъпки програмата се премества от мястото където е била в мястото където искате да бъде. За да могат тези често повтарящи се процеси да бъдат практични, тестовете трябва да работят много бързо или софтуерният инженер ще трябва да прекара голяма част от времето си в изчакване на завършването на тестовете. Привържениците на екстремното програмиране и други гъвкави методологии описват тази дейност като неразделна част от цикъла за разработване на софтуер.

Видове рефакторинг техники

[редактиране | редактиране на кода]

Тук са показани някои примери за микро-рефакторинги; някои от тях могат да се прилагат само за определени езици или видове езици. По-дълъг списък може да се намери в книгата на Мартин Фаулър[3] както и сайта на автора.[5] Много среди за разработка осигуряват автоматизирани методи за тези микро-рефакторинги. Например, един програмист може да кликне върху името на променливата и след това да избере „Encapsulate field“ refactoring от контекстното меню. Интегрираната среда за разработка (на английски: integrated development environment, IDE) e софтуерно приложение, което предоставя цялостна среда на програмистите за разработване на софтуер. IDE може да подскаже допълнителни детайли, които обикновено с подходящи подразбиращи се стойности и визуализация да покажат промените в кода. След потвърждение от страна на програмиста, ще се извършват необходимите промени в целия код.

  • Техники, които позволяват по-голяма абстракция (на английски: abstraction)
  • Encapsulate field (програмиране на полето) – наричано също и „скриване на информация“: Прави невъзможно за потребителите на даден обект да променят неговото вътрешно състояние по неочакван начин; в случай, ако има в кода открито поле, то трябва да стане недостъпно и да се обезпечи достъпа до него.
  • Обобщаващ тип – създаване на повече обобщаващи типове, чрез които да се даде възможност за повече споделяне на кодове.
  • Сменете типа на проверяващия код с Условия/Стратегия.[6]
  • Сменете зависимостта от условия с полиморфизъм.[7]
  • Техники за разделяне на кода на съставни части с цел по-голяма разбираемост
  • Компонентността на разделянето на кода в многократно използвани семантични единици, което се изразява в ясно и добре дефиниран, прост за използване интерфейс.
  • Extract Class – премества част от код от съществуващ клас в нов клас.
  • Extract Method – за да включите част от по-голям метод в нов метод. Чрез разделянето на кода на по-малки парчета, той става по-лесно разбираем. Това се прилага също и при функции.
  • Техники за подобряване на името и местоположението на кода
  • Метод на преместване или преместване на поле – да се премести в по-подходящ клас или изходен код (на английски: source file).
  • Преименуване на метод или преименуване на поле – даване на ново име, което по-добре разкрива неговата цел.
  • Изтеглен нагоре – в OOП (обектно-ориентирано програмиране), преместване в суперклас (на английски: superclass).
  • Преместен надолу – в OOП (обектно-ориентирано програмиране), преместване в подклас (на английски: subclass).

Хардуерен рефакторинг

[редактиране | редактиране на кода]

Въпреки че, терминът рефакторинг първоначално се е отнасял изключително за рефакторинг на софтуерен код, през последните години код се споменава и в хардуерно описващи езици (HDL), в които също се извършва рефакторинг. Терминът хардуерен рефакторинг се използва като термин за рефакторинг на код на езици за хардуерно описание. Хардуерния рефакторинг трябва да се разглежда като нещо отделно, тъй като повечето хардуерни инженери не считат HDLs за програмни езици.

Автоматизирания рефакторинг на аналогово описания хардуер (в VHDL-AMS) е предложен от Zeng and Huss.[8] В техния подход рефакторинг запазва симултативното поведение на хардуерния замисъл. Нефункционалните измервания, които подобряват рефакторирания код могат да бъдат обработени чрез стандартни инструменти Synthesis, докато при оригиналния код не може да се осъществи такава обработка. Рефакторинг на цифрови (дигитални) HDLs, макар и с ръчен рефакторинг, също е бил изследван от синопсис изследователя Mike Keating.[9][10]. Неговата цел е да се направят комплексните системи по-лесно разбираеми, което води до повишаване на производителността на създателите им.

През лятото на 2008 г., е имало интензивна дискусия за рефакторинг на VHDL (VHDL е език за структурно и поведенческо описание на цифрови системи). Английският акроним за VHDL е VHSIC Hardware Description Language, където VHSIC – Very High Scale Integrated Circuit. В превод на български означава Език за описание на хардуер на много големи интегрални схеми. В действителност, VHDL се използва за описание както на малки и прости цифрови системи, така и на изключително сложни такива. С негова помощ е възможно да бъде описан проект на няколко различни нива на абстракция – поведенческо (behavioral), регистрово (data flow, RTL), структурно (structural)). – news://comp.lang.vhdl newsgroup.[11] Дискусията се върти около конкретен рефакторинг извършван от един инженер, както и за това дали да има или не автоматизирани инструменти за рефакторинг.

Към края на 2009 г., Sigasi предлага разработка, която поддържа VHDL рефакторинг.[12]

AMIQ DVT, интегрирана среда за хардуерно проектиране и проверка, осигурява възможности за рефакторинг в E, SystemVerilog, Verilog и VHDL.

В миналото refactoring не е допускан в процесите на развитие. Пример за това е CVS централизирана система за контрол на версиите, позволяваща на няколко разработчици да работят върху един и същ проект. CVS е свободен софтуер, създаден през 1984.

Въпреки че рефакторинг код е правен неофициално в продължение на години, първият голям академичен труд за функциалността на рефакторинг и процедурните програми е докторската дисертация [13] на William Griswold's 1991 Ph.D., последвана от дисертация на William Opdyke[14] през 1992 година на тема рефакторинг на обектно-ориентирани програми,[15]. Въпреки че цялата теория и механизъм отдавна са били на разположение под формата на системи за програмно трансформиране. Всичко гореспоменато осигурява един списък от най-често използваните методи за рефакторинг.

Книгата на Мартин Фаулър Refactoring: Improving the Design of Existing Code[3] е общопризнат библиографски източник.

Първата известна употреба на термина „рефакторинг“ е в статията на William F. Opdyke и Ralph E. Johnson.[16], публикувана през септември 1990 година. В своите дисертационни трудове Griswold[13], публикуван през 1991 г., и Opdyke[14], публикуван през 1992 г., също използват този термин.[15]

Най-рано терминът „факторинг“ е бил използван в средите, използващи Forth в началото на 80-те години на ХХ век. Шеста глава на книгата на Leo Brodie Thinking Forth (1984) е посветена на този въпрос.

В екстремното програмиране Extract Method като рефакторинг техника по същество има същото значение като факторинг в Forth; да направят една „дума“ (или функция) на по-малки, по-лесно поддържани функции.

Рефакторингът може да се разглежда и като модифициран последващ анализ (posthoc – от латински post hoc – „след това“), давайки кратки описания на по-сложни софтуерни промени, записани в софтуерни хранилища като CVS (Concurrent Versions System) или SVN.

Практически аспекти

[редактиране | редактиране на кода]

Ето пример на рефакторинг чрез прилагането на т.нар. extract method. [17]

Създаваме нов метод чрез отделяне на част от съществуващия код. За целта използваме следния пример:

public void report(Writer out, List machines) throws IOException {
  for (Machine machine: machines) {
    out.write("Machine " + machine.name());
    if (machine.bin() != null)
      out.write(" bin=" + machine.bin());
    out.write("\n");
  }
}

Искаме да отделим кода относно всяка машина.

Стъпка 1: Създаваме нов празен метод.

private void reportMachine() {
}

Важно е да се избере име, което отразява целта на новия метод, а не неговото изпълнение.

Стъпка 2: Копираме кода в новия метод.

private void reportMachine() {
  out.write("Machine " + machine.name());
  if (machine.bin() != null)
    out.write(" bin=" + machine.bin());
  out.write("\n");
}

В примера ни, създаваме reportMachine-метод, който не се компилира. Забележете, че към момента първоначалния метод остава непроменен.

Стъпка 3: Добавяме параметрите на променливите

За всяка от временните променливи, използвани в копирания код, добавяме параметър към новия метод:

private void reportMachine(Writer out, Machine machine) {
  out.write("Machine " + machine.name());
  if (machine.bin() != null)
    out.write(" bin=" + machine.bin());
  out.write("\n");
}

Също трябва да обработим изключенията, хвърлени от кода:

private void reportMachine(Writer out, Machine machine) throws IOException {
  out.write("Machine " + machine.name());
  if (machine.bin() != null)
    out.write(" bin=" + machine.bin());
  out.write("\n");
}

След всяка промяна може да си проверяваме кода чрез компилация. Едва когато новия метод се компилира чисто, едва тогава може да се счита, че сме приключили.

Стъпка 4: Извикване на новия метод

Най-накрая може да заменим копирания код в първоначалния метод чрез извикване на новия метод:

public void report(Writer out, List machines) throws IOException {
  for (Machine machine: machines) {
    reportMachine(out, machine);
  }
}

Компилираме, тестваме и сме готови.

Рефакториране посредством extract method може да бъде и малко по-сложно, ако кода, който искаме да отделим променя временните променливи. Нека да разгледаме следния код, който е изменена версия на предишния ни пример.

public String report(List machines) {
  String result = "";
  for (Machine machine: machines) {
    result += "Machine " + machine.name();
    if (machine.bin() != null)
    	result += " bin=" + machine.bin();
    result += "\n";
  }
  return result;
}

Тук, кодът който искаме да отделим променя резултатните временни променливи. В този случай, при стъпка 3 трябва да декларираме нов резултат в новия метод и да върнем неговата стойност в края на изчислението:

private String reportMachine(Machine machine) {
  String result = "Machine " + machine.name();
  if (machine.bin() != null)
  	result += " bin=" + machine.bin();
  result += "\n";
  return result;
}

Съответно, при стъпка 4 сега трябва да използваме върнатия резултат:

public String report(List machines) {
  String result = "";
  for (Machine machine: machines) {
	result += reportMachine(machine);
  }
  return result;
}

Нека да разгледаме още един пример, описан от Мартин Фаулър на сайта му.[18]

void printOwing() {
		printBanner();

		//print details
		System.out.println ("name:	" + _name);
		System.out.println ("amount	" + getOutstanding());
	}

В примера част от кода се превръща в метод, чието име пояснява целта на метода.

void printOwing() {
		printBanner();
		printDetails(getOutstanding());
	}

	void printDetails (double outstanding) {
		System.out.println ("name:	" + _name);
		System.out.println ("amount	" + outstanding);
	}

Автоматизиран рефакторинг

[редактиране | редактиране на кода]

Много софтуерни редактори и интегрирани среди за разработка имат автоматично поддържат рефакторинг. Ето списък на някои от тези редактори или наречени още рефакторинг браузъри.

  1. What is Refactoring?, Martin Fowler, refactoring.com/
  2. а б Kerievsky, Joshua (2004). Refactoring to Patterns, Publisher: Addison-Wesley Professional, ISBN-10: 0321213351
  3. а б в Fowler, Martin (1999). Refactoring: Improving the design of existing code. Addison Wesley.
  4. Martin, Robert (2009). Clean Code. Prentice Hall.
  5. Refactoring techniques in Fowler's refactoring Website
  6. Replace type-checking code with State/Strategy
  7. Replace conditional with polymorphism
  8. Kaiping Zeng, Sorin A. Huss, „Architecture refinements by code refactoring of behavioral VHDL-AMS models“. ISCAS 2006
  9. M. Keating:"Complexity, Abstraction, and the Challenges of Designing Complex Systems", in DAC'08 tutorial, (1) Архив на оригинала от 2016-03-28 в Wayback Machine., Bridging a Verification Gap: C++ to RTL for Practical Design"
  10. M. Keating, P. Bricaud: Reuse Methodology Manual for System-on-a-Chip Designs, Kluwer Academic Publishers, 1999.
  11. newsgroups.derkeiler.com, архив на оригинала от 27 септември 2013, https://web.archive.org/web/20130927082115/http://newsgroups.derkeiler.com/Archive/Comp/comp.lang.vhdl/2008-06/msg00173.html, посетен на 30 август 2013 
  12. Sigasi launches its first production release for a VHDL development environment
  13. а б Griswold, William G (July 1991).Program Restructuring as an Aid to Software Maintenance Ph.D. thesis University of Washington. Посетен на 24 декември 2011.
  14. а б Opdyke, William F (June 1992). Refactoring Object-Oriented Frameworks[неработеща препратка](compressed Postscript). Ph.D. thesis. University of Illinois at Urbana-Champaign. Посетен на 12 февруари 2008.
  15. а б Martin Fowler, „MF Bliki: EtymologyOfRefactoring“
  16. William Opdyke, Ralph E. (September 1990). „Refactoring: An Aid in Designing Application Frameworks and Evolving Object-Oriented Systems“. Proceedings of the Symposium on Object Oriented Programming Emphasizing Practical Applications (SOOPPA). ACM].
  17. Kevin Rutherford, PhD, Refactoring Patterns
  18. Martin Fowler, Extract Method
  Тази страница частично или изцяло представлява превод на страницата Code refactoring в Уикипедия на английски. Оригиналният текст, както и този превод, са защитени от Лиценза „Криейтив Комънс – Признание – Споделяне на споделеното“, а за съдържание, създадено преди юни 2009 година – от Лиценза за свободна документация на ГНУ. Прегледайте историята на редакциите на оригиналната страница, както и на преводната страница, за да видите списъка на съавторите. ​

ВАЖНО: Този шаблон се отнася единствено до авторските права върху съдържанието на статията. Добавянето му не отменя изискването да се посочват конкретни източници на твърденията, които да бъдат благонадеждни.​